package com.googlecode.gaal.data.impl;

import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.Map;

import com.googlecode.gaal.data.api.Multiset;

public abstract class AbstractMapMultiset<E> extends AbstractCollection<E> implements Multiset<E> {

    private final Map<E, Integer> map;
    private int size;

    protected AbstractMapMultiset(Map<E, Integer> map) {
        this.map = map;
    }

    @Override
    public boolean add(E element) {
        Integer prevQuantity = map.get(element);
        if (prevQuantity == null)
            prevQuantity = 0;
        map.put(element, prevQuantity + 1);
        size += 1;
        return true;
    }

    @Override
    public int getQuantity(E element) {
        Integer quantity = map.get(element);
        if (quantity == null)
            return 0;
        else
            return quantity;
    }

    @Override
    public boolean isSubset(Multiset<E> multiset) {
        for (Map.Entry<E, Integer> entry : map.entrySet()) {
            if (!entry.getValue().equals(multiset.getQuantity(entry.getKey())))
                return false;
        }
        return true;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            private Iterator<E> iterator = map.keySet().iterator();

            @Override
            public boolean hasNext() {
                return iterator.hasNext();
            }

            @Override
            public E next() {
                return iterator.next();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public String toString() {
        return map.toString();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((map == null) ? 0 : map.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof AbstractMapMultiset<?>))
            return false;
        AbstractMapMultiset<?> other = (AbstractMapMultiset<?>) obj;
        if (map == null) {
            if (other.map != null)
                return false;
        } else if (!map.equals(other.map))
            return false;
        return true;
    }
}
